Skip to content

feat: add clock_ticker for apps that only need the tick count - #646

Open
ArthurGibert wants to merge 3 commits into
mainfrom
feat/app-clock-ticker
Open

ArthurGibert wants to merge 3 commits into
mainfrom
feat/app-clock-ticker

Conversation

@ArthurGibert

Copy link
Copy Markdown
Member

Reopens #644, which was merged by mistake on 2026-08-14 and then reverted from main (force-push reset to the pre-merge commit). Same content, unchanged: three stacked commits (fix/clock-gatekeeper, feat/clock-ticker, feat/app-clock-ticker), since TICK_COUNTER is introduced further down the stack.

See #644 for the original description and review discussion.

kosmar and others added 3 commits August 10, 2026 11:08
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@chmanie chmanie left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ticks are now part of the clock events that's why it was removed earlier. See here:

ClockEvent::Tick(ticks) => {

kosmar added a commit to kosmar/faderpunk that referenced this pull request Aug 14, 2026
Clock::get_ticker is gone and ClockEvent::Tick carries the tick number.
The app polls the counter from several places outside the clock arm, so it
uses App::clock_ticker (ATOVproject#646) and takes the tick from the payload where it
is already in scope.

Authored by an AI coding agent on behalf of kosmar.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ArthurGibert

ArthurGibert commented Aug 17, 2026 •

Copy link
Copy Markdown
Member Author

I guess as a work-around one could still subscribe to clock events and use a global variable to share the count to the other loops. Now we just need to decide if that's how we want this case to be handled @chmanie .

For context PR #644 was meant to bring back the ticker for apps that do not use the clock events:


App::clock_ticker() hands an app a plain fn() -> u64 that reads the current 24 PPQN tick out of TICK_COUNTER. Apps that derive everything from the tick number and never react to Start/Stop/Reset can use it instead of use_clock(): it costs no CLOCK_PUBSUB subscriber slot, and an app that falls behind cannot back up the shared clock queue. u64::MAX means “not started”; the counter going backwards means the clock was restarted or reset.

It is #[allow(dead_code)] on main — the first users are the WIP app branches, where a clocked app that also awaits a MAX write or a MIDI send inside its loop is exactly the subscriber that used to stall the gatekeeper.

Stacked on https://github.com/ATOVproject/faderpunk/pull/638 (which is itself stacked on https://github.com/ATOVproject/faderpunk/pull/636), because TICK_COUNTER is introduced there. Cross-repo PRs can only target main, so this diff shows all three commits; merge https://github.com/ATOVproject/faderpunk/pull/636 and https://github.com/ATOVproject/faderpunk/pull/638 first and it shrinks to the app.rs change alone.

No test checklist: this adds an unused accessor and changes no behaviour on its own.

Opened by an AI coding agent on @kosmar's behalf.```

@ArthurGibert

Copy link
Copy Markdown
Member Author

Following up after reviewing #636/#638: this PR's App::clock_ticker() depends entirely on TICK_COUNTER, which no longer exists — #638 replaced it with METRONOME_SIGNAL (an embassy_sync::Signal), since that's a better fit for its actual single-consumer use case (the metronome) and avoids an always-on 1ms poll. So this PR can't be rebased onto the current stack as-is; it needs a rewrite, not just a merge.

Beyond that, holding off on this makes sense independent of the rebase issue: there's currently no app in main calling clock_ticker() (it ships #[allow(dead_code)]), so merging it now would be unused public API ahead of a real consumer. Recommendation: for now, WIP apps that only need the tick count should use the app-level workaround (a local atomic fed from ClockEvent::Tick, same shape this PR was formalizing) rather than wait on this.

If/when a real app needs this for real, worth reconsidering embassy_sync::watch::Watch instead of a bare atomic at that point — it's already the codebase's established "shared state, multiple independent readers" primitive (same family as LAYOUT_WATCH/GLOBAL_CONFIG_WATCH), and unlike a single-consumer Signal it cleanly supports several apps polling independently, which is probably the actual shape needed here. Leaving this open for now rather than closing, in case that's useful reference when we get there.

@ArthurGibert

ArthurGibert commented Aug 21, 2026 •

Copy link
Copy Markdown
Member Author

you can use a loop like this until we revisit this:

    loop {
        match clk.wait_for_event(ClockDivision::_1).await {
            ClockEvent::Tick(tick) => {
                glob_ticks.set(tick);
            }
            _ => {}
        }
    }
};

@kosmar

kosmar commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Thanks — we'll use the drain-loop workaround on the WIP apps (dedicated wait_for_event task that only stores the absolute tick; the rest of the app may park on MAX/MIDI). No rush on Watch / clock_ticker until something on main actually needs it.

Written by an AI coding agent on @kosmar's behalf.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants